
package model;

import java.util.HashMap;
import java.util.LinkedList;
import java.util.Map;
import java.util.Queue;
import java.util.concurrent.ArrayBlockingQueue;

public class Scheduler
{
  /**
   * The ready queue.
   */
  private Queue<Process> my_ready_queue;

  /**
   * The map of IO blocked queues.
   */
  private Map<Integer, Queue<Process>> my_map_of_io_blocked_queue;

  /**
   * The map of semaphore blocked queues.
   */
  private Map<Semaphore, Queue<Process>> my_map_of_sem_blocked_queue;

  /**
   * Construct an empty Scheduler.
   */
  public Scheduler()
  {
    my_ready_queue = new ArrayBlockingQueue<Process>(100);
    my_map_of_io_blocked_queue = new HashMap<Integer, Queue<Process>>();
    my_map_of_sem_blocked_queue = new HashMap<Semaphore, Queue<Process>>();
  }

  /**
   * The next process in the ready queue is removed and returned.
   * 
   * @return The next process in the ready queue.
   */
  public Process getNextReadyProcess()
  {
    return my_ready_queue.poll();
  }

  /**
   * The next process in the IO blocked queue is removed and returned.
   * 
   * @param the_id The ID of the IO device.
   * @return The next process in the blocked queue for this IO device.
   */
  public Process getNextProcessInIOBQ(final int the_id)
  {
    return my_map_of_io_blocked_queue.get(the_id).poll();
  }

  /**
   * The next process in the semaphore blocked queue is removed and returned.
   * 
   * @param the_sem The semaphore.
   * @return The next process in the blocked queue for this semaphore.
   */
  public Process getNextProcessInSemBQ(final Semaphore the_sem)
  {
    return my_map_of_sem_blocked_queue.get(the_sem).poll();
  }

  /**
   * Add the process to the ready queue.
   * 
   * @param the_process The process.
   */
  public void addProcessToReadyQ(final Process the_process)
  {
    try
    {
      ((ArrayBlockingQueue<Process>) my_ready_queue).put(the_process);
    }
    catch (InterruptedException e)
    {
      System.err.println("Interrupted exception in Scheduler.addProcessToReadyQ");
      e.printStackTrace();
    }
  }

  /**
   * Add the process to the IO blocked queue for the IO with the specified ID.
   * 
   * @param the_id The ID of the IO device.
   * @param the_process The process.
   */
  public void addProcessToIOBQ(final int the_id, final Process the_process)
  {
    try
    {
      ((ArrayBlockingQueue<Process>) my_map_of_io_blocked_queue.get(the_id)).put(the_process);
    }
    catch (InterruptedException e)
    {
      System.err.println("Interrupted exception in Scheduler.addProcessToIOBQ");
      e.printStackTrace();
    }
  }

  /**
   * Add the process to the semaphore blocked queue for the semaphore with the
   * specified ID.
   * 
   * @param the_sem The semaphore.
   * @param the_process The process.
   */
  public void addProcessToSemBQ(final Semaphore the_sem, final Process the_process)
  {
    try
    {
      ((ArrayBlockingQueue<Process>) my_map_of_sem_blocked_queue.get(the_sem)).put(the_process);
    }
    catch (InterruptedException e)
    {
      System.err.println("Interrupted exception in Scheduler.addProcessToSemBQ");
      e.printStackTrace();
    }
  }

  /**
   * Create a blocked queue for this semaphore.
   * 
   * @param the_sem The semaphore.
   */
  public void createSemBQ(final Semaphore the_sem)
  {
    my_map_of_sem_blocked_queue.put(the_sem, new ArrayBlockingQueue<Process>(100));
  }

  /**
   * Create the blocked queue for this IO.
   * 
   * @param the_id The IO's id.
   */
  public void createIOBQ(final int the_id)
  {
    my_map_of_io_blocked_queue.put(the_id, new ArrayBlockingQueue<Process>(100));
  }

  /**
   * 
   * @return True if the ready queue is empty. False otherwise.
   */
  public boolean isReadyQueueEmpty()
  {
    return my_ready_queue.isEmpty();
  }

  /**
   * 
   * @param the_id The ID of the IO device.
   * @return True if the IO blocked queue for this device is empty. False
   *         otherwise.
   */
  public boolean isIOBQempty(final int the_id)
  {
    return my_map_of_io_blocked_queue.get(the_id).isEmpty();
  }

  /**
   * 
   * @param the_sem The semaphore.
   * @return True if the IO blocked queue for this semaphore is empty. False
   *         otherwise.
   */
  public boolean isSemBQempty(final Semaphore the_sem)
  {
    return my_map_of_sem_blocked_queue.get(the_sem).isEmpty();
  }

  /**
   * @return The current report for this scheduler.
   */
  public String toString()
  {
    synchronized (this)
    {
    StringBuffer result = new StringBuffer(200);
    result.append("The current ready queue (the front of the queue is on the left) : ");
    for (Process a_process : my_ready_queue)
    {
      result.append(a_process.toString() + " - ");
    }
    result.append("\nThe current blocked queue for the 0 IO device : ");
    for (Process a_process : my_map_of_io_blocked_queue.get(0))
    {
      result.append(a_process.toString() + " - ");
    }
    result.append("\nThe current blocked queue for the 1 IO device : ");
    for (Process a_process : my_map_of_io_blocked_queue.get(1))
    {
      result.append(a_process.toString() + " - ");
    }
    result.append("\n");

    for (Map.Entry<Semaphore, Queue<Process>> an_entry : my_map_of_sem_blocked_queue
        .entrySet())
    {
      result.append("The current blocked queue for " + an_entry.getKey().toString() + " : ");
      for (Process a_process : an_entry.getValue())
      {
        result.append(a_process.toString() + " - ");
      }
      result.append("\n");
    }

    return result.toString();
    }
  }
}
